package org.infodavid.persistence.impl.hibernate.usertype;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserVersionType;
import org.infodavid.common.util.TimeUtil;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.joda.time.contrib.hibernate.PersistentDateTime;

/**
 * The Class DateTimeUserType.
 */
public final class LocalDateTimeUserType extends PersistentDateTime implements UserVersionType {

  /** The Constant LOGGER. */
  private static final Log LOGGER = LogFactory.getLog(LocalDateTimeUserType.class);

  /** serialVersionUID. */
  private static final long serialVersionUID = -2551243433609810663L;

  /*
   * (non-Javadoc)
   * @see org.joda.time.contrib.hibernate.PersistentDateTime#equals(java.lang.Object,
   * java.lang.Object)
   */
  @Override
  public boolean equals(final Object x, final Object y) throws HibernateException {
    return TimeUtil.getInstance().equals((LocalDateTime)x, (LocalDateTime)y);
  }

  /*
   * (non-Javadoc)
   * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
   */
  @Override
  public int compare(final Object x, final Object y) {
    return TimeUtil.getInstance().compare((LocalDateTime)x, (LocalDateTime)y);
  }

  /*
   * (non-Javadoc)
   * @see org.hibernate.usertype.UserVersionType#next(java.lang.Object,
   * org.hibernate.engine.SessionImplementor)
   */
  @Override
  public Object next(final Object current, final SessionImplementor session) {
    LocalDateTime next;

    if (current == null) {
      next = new LocalDateTime(DateTimeZone.UTC);
    }
    else {
      final LocalDateTime previous = (LocalDateTime)current;
      next = new LocalDateTime(DateTimeZone.UTC);

      // bug: sometimes the data time is the same as the passed one.
      if (next.equals(previous)) {
        next = next.plusMillis(50);
      }
    }

    // System.out.println("current =" + current);
    // System.out.println("next =" + next);

    if (LOGGER.isTraceEnabled()) {
      LOGGER.trace("Next is " + next + " using \n" + current);
    }

    return next;
  }

  /*
   * (non-Javadoc)
   * @see org.hibernate.usertype.UserVersionType#seed(org.hibernate.engine.SessionImplementor)
   */
  @Override
  public Object seed(final SessionImplementor session) {
    return next(null, session);
  }

  /*
   * (non-Javadoc)
   * @see org.joda.time.contrib.hibernate.PersistentDateTime#nullSafeGet(java.sql.ResultSet,
   * java.lang.String)
   */
  @Override
  public Object nullSafeGet(final ResultSet resultSet, final String string) throws SQLException {
    final Object timestamp = super.nullSafeGet(resultSet, string);
    LocalDateTime data;

    if (timestamp == null) {
      data = null;
    }
    else {
      data = new LocalDateTime(timestamp, DateTimeZone.UTC);
    }

    if (LOGGER.isTraceEnabled()) {
      LOGGER.trace("Getting date " + data + " using \n" + string);
    }

    return data;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.joda.time.contrib.hibernate.PersistentDateTime#nullSafeSet(java.sql.PreparedStatement,
   *      java.lang.Object, int)
   */
  @Override
  public void nullSafeSet(final PreparedStatement preparedStatement, final Object value,
      final int index) throws HibernateException, SQLException {
    Date data;

    if (value == null) {
      preparedStatement.setNull(index, sqlTypes()[7]);
    }
    else {
      final LocalDateTime date = (LocalDateTime)value;

      data = date.toDateTime(DateTimeZone.UTC).toDate();

      if (LOGGER.isTraceEnabled()) {
        LOGGER.trace("Setting date " + data + " using \n" + value);
      }

      super.nullSafeSet(preparedStatement, data, index);
    }
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.joda.time.contrib.hibernate.PersistentDateTime#deepCopy(java.lang.Object)
   */
  @Override
  public Object deepCopy(final Object value) throws HibernateException {
    Object data;

    if (value instanceof LocalDateTime) {
      data = value;
    }
    else {
      data = null;
    }

    return data;
  }

  /*
   * (non-Javadoc)
   * @see org.joda.time.contrib.hibernate.PersistentDateTime#fromXMLString(java.lang.String)
   */
  @Override
  public Object fromXMLString(final String string) {
    return new DateTime(string);
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.hibernate.usertype.UserType#nullSafeGet(java.sql.ResultSet, java.lang.String[],
   *      org.hibernate.engine.spi.SessionImplementor, java.lang.Object)
   */
  @Override
  public Object nullSafeGet(ResultSet arg0, String[] arg1, SessionImplementor arg2, Object arg3)
      throws HibernateException, SQLException {
    return nullSafeGet(arg0, arg1, arg3);
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @see org.hibernate.usertype.UserType#nullSafeSet(java.sql.PreparedStatement, java.lang.Object,
   *      int, org.hibernate.engine.spi.SessionImplementor)
   */
  @Override
  public void nullSafeSet(PreparedStatement arg0, Object arg1, int arg2, SessionImplementor arg3)
      throws HibernateException, SQLException {
    nullSafeSet(arg0, arg1, arg2);
  }
}
